package com.jzo2o.common.utils;

import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SpEL表达式解析工具类，用于解析模板中的SpEL表达式并替换为实际参数值
 */
public class SpelUtils {

    /**
     * 匹配SpEL表达式的正则（格式：#{表达式}）
     */
    private static final Pattern PATTERN = Pattern.compile("(\\#\\{([^\\}]*)\\})");

    /**
     * 将模板中的表达式替换成args参数中的值
     *
     * @param formatter   模板
     * @param paraNameArr 方法对应的参数名称
     * @param args        方法参数值value，用来进行退换对应的表达式
     * @return 模板替换后的字符串
     * <p>
     * 例    format : counter:#{user.id}
     * paraNameAddr [user]
     * args [{"user":{"id":1}}]
     * <p>
     * 转换后结果 -> counter:1
     */
    public static String parse(String formatter, String[] paraNameArr, Object[] args) {
        // 模板为空或无SpEL表达式时直接返回null
        if (StringUtils.isNotBlank(formatter) && formatter.indexOf("#") > -1) {

            // 用正则匹配模板中的所有SpEL表达式（#{...}格式）
            Matcher matcher = PATTERN.matcher(formatter);
            //将正则表达式中#{}的值取出放在keys中
            List<String> keys = new ArrayList<>();
            while (matcher.find()) {
                keys.add(matcher.group());
            }
            // 若存在匹配的表达式，进行解析替换
            if (!CollUtils.isEmpty(keys)) {
                //SPEL表达式对象
                ExpressionParser parser = new SpelExpressionParser();
                StandardEvaluationContext context = new StandardEvaluationContext();
                //将名称和value一一对应-----将参数名与参数值绑定到SpEL上下文中，供表达式解析使用
                for (int i = 0; i < paraNameArr.length; i++) {
                    context.setVariable(paraNameArr[i], args[i]);
                }

                // 遍历每个匹配到的表达式，替换为解析后的实际值
                for (String tmp : keys) {
                    formatter = formatter.replace(tmp,
                            // 提取表达式内容（去掉#{和}），并加上"#"转为SpEL变量表达式
                            // 解析表达式获取对应值，再替换模板中的原表达式
                            parser.parseExpression("#" + tmp.substring(2, tmp.length() - 1)).getValue(context, String.class));
                }
                return formatter;
            }
        }
        return null;
    }

}
